package com.shareware.common.secure.hash

import android.os.Build
import com.shareware.common.utils.hexToByteArray
import com.shareware.common.utils.log
import com.shareware.common.utils.toHexString
import java.security.SecureRandom
import javax.crypto.SecretKeyFactory
import javax.crypto.spec.PBEKeySpec

/**
 * desc: 功能描述
 * 密码 加密/验证
 * 比Md5更安全
 * email: mobiledeveloper@qq.com
 */
class PBKDF2 private constructor() {

    companion object {
        //迭代默认次数
        private const val ENCODE_DEFAULT_TIMES = 10000

        //密码最小长度
        private const val KEY_LENGTH = 32

        //安全随机码位数
        private const val RANDOM_SIZE = 16

        private const val TRANSFORMATION_SHA1 = "PBKDF2WithHmacSHA1"
        private const val TRANSFORMATION_SHA256 = "PBKDF2WithHmacSHA256"
        private const val TAG = "PBKDF2"

        fun encryptPassword(password: String): String {
            return encryptPassword(password, ENCODE_DEFAULT_TIMES)
        }

        fun encryptPassword(password: String, times: Int): String {
            val randomBytes = generateSecureRandom()
            return encryptPassword(password, randomBytes, times)
        }

        private fun encryptPassword(password: String, randomBytes: ByteArray, iterations: Int): String {
            if (password.isEmpty()) {
                log(TAG, "pwd is empty")
                return ""
            }
            val cipherBytes = try {
                val secretKeyFactory = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
                    log(TAG, "sha 1")
                    SecretKeyFactory.getInstance(TRANSFORMATION_SHA1)
                } else {
                    log(TAG, "sha 256")
                    SecretKeyFactory.getInstance(TRANSFORMATION_SHA256)
                }
                val pbeKeySpec = PBEKeySpec(password.toCharArray(), randomBytes, iterations, KEY_LENGTH * 8)
                secretKeyFactory.generateSecret(pbeKeySpec).encoded
            } catch (ex: Exception) {
                byteArrayOf()
            }
            return randomBytes.toHexString() + cipherBytes.toHexString()
        }

        fun validatePassword(password: String, cipherText: String): Boolean {
            return validatePassword(password, cipherText, ENCODE_DEFAULT_TIMES)
        }

        fun validatePassword(password: String, cipherText: String, times: Int): Boolean {
            return if (cipherText.length > KEY_LENGTH && password.isNotEmpty() && cipherText.isEmpty()) {
                val randomBytes = cipherText.substring(0, KEY_LENGTH)
                val newCipherText = encryptPassword(password, randomBytes.hexToByteArray(), times)
                return cipherText == newCipherText
            } else {
                false
            }
        }

        private fun generateSecureRandom(): ByteArray {
            return try {
                val random = SecureRandom()
                val numBytes = ByteArray(RANDOM_SIZE)
                random.nextBytes(numBytes)
                numBytes
            } catch (ignore: Exception) {
                byteArrayOf()
            }
        }
    }
}